using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;

using HundredMilesSoftware.UltraID3Lib;


namespace MP3TagBackup
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                if (args.Length > 0 && args[0].Equals("-?"))
                {
                    Console.WriteLine("Usage:  MP3TagBackup [outputfile] [directory]");
                    Console.WriteLine("  Creates an XML file containing the ID3 tags from all .mp3 files in the directory.");
                    Console.WriteLine("Default outputfile is mp3tags.xml.  Default directory is the current directory.");
                    Console.WriteLine();
                    Console.WriteLine("MP3TagBackup uses UltraID3Lib, an MP3 ID3 Tag Editor and MPEG Info Reader Library");
                    Console.WriteLine("Copyright 2002 - 2006 Hundred Miles Software (Mitchell S. Honnert)");
                    Console.WriteLine("www.UltraID3Lib.com, www.HundredMilesSoftware.com");
                    return;
                }

                string outputFile = (args.Length > 0) ? args[0] : "mp3tags.xml";
                string directory = (args.Length > 1) ? args[1] : Directory.GetCurrentDirectory();

                string[] files = Directory.GetFiles(directory, "*.mp3");
                if (files.Length == 0)
                {
                    Console.WriteLine("No MP3 files found.  (use -? switch for help)");
                    return;
                }

                XmlDocument doc = new XmlDocument();
                XmlElement rootElement = doc.CreateElement("mp3files");

                foreach (string file in files)
                {
                    Console.WriteLine("Reading tags from file " + file);

                    XmlElement fileElement = doc.CreateElement("mp3file");
                    fileElement.SetAttribute("filename", file);

                    UltraID3 tags = new UltraID3();
                    tags.Read(file);
                    foreach (ID3Frame frame in tags.ID3v23Tag.Frames)
                    {
                        XmlElement frameElement = CreateFrameElement(doc, frame);
                        if (frameElement != null)
                        {
                            fileElement.AppendChild(frameElement);
                        }
                        else
                        {
                            Console.WriteLine("Unsupported ID3 frame: " + frame);
                        }
                    }
                    rootElement.AppendChild(fileElement);
                }
                doc.AppendChild(rootElement);
                doc.Save(outputFile);
            }
            catch (Exception ex)
            {
                Console.WriteLine("Exception: " + ex.Message);
                Console.WriteLine(ex.StackTrace);
            }
        }



        private static XmlElement CreateFrameElement(XmlDocument doc, ID3Frame frame)
        {
            XmlElement frameElement = null;
            switch (frame.FrameType)
            {
                case FrameTypes.Unknown:
                    break;

                case FrameTypes.Artist:
                    frameElement = doc.CreateElement("artist");
                    frameElement.InnerText = ((ID3ArtistFrame)frame).Artist;
                    break;

                case FrameTypes.Album:
                    frameElement = doc.CreateElement("album");
                    frameElement.InnerText = ((ID3AlbumFrame)frame).Album;
                    break;

                case FrameTypes.Year:
                    frameElement = doc.CreateElement("year");
                    frameElement.InnerText = ((ID3YearFrame)frame).Year.ToString();
                    break;

                case FrameTypes.Genre:
                    frameElement = doc.CreateElement("genre");
                    frameElement.InnerText = ((ID3GenreFrame)frame).Genre;
                    break;

                case FrameTypes.AudioEncryption:
                    break;

                case FrameTypes.Picture:
                    break;

                case FrameTypes.Comments:
                    frameElement = doc.CreateElement("album");
                    frameElement.SetAttribute("description", ((ID3CommentsFrame)frame).Description);
                    frameElement.InnerText = ((ID3CommentsFrame)frame).Comments;
                    break;

                case FrameTypes.Commercial:
                    break;

                case FrameTypes.EncryptionMethodRegistration:
                    break;

                case FrameTypes.Equalization:
                    break;

                case FrameTypes.EventTimingCodes:
                    break;

                case FrameTypes.GeneralEncapsulatedObject:
                    break;

                case FrameTypes.GroupIdentificationRegistration:
                    break;

                case FrameTypes.InvolvedPeopleList:
                    frameElement = doc.CreateElement("involvedPeople");
                    foreach (InvolvedPerson person in ((ID3InvolvedPeopleListFrame)frame).InvolvedPersonList)
                    {
                        XmlElement personElement = doc.CreateElement("involvedPerson");
                        personElement.SetAttribute("type", person.InvolvementType);
                        personElement.InnerText = person.PersonName;
                        frameElement.AppendChild(personElement);
                    }
                    break;

                case FrameTypes.LinkedInformation:
                    break;

                case FrameTypes.CompactDiscIdentifier:
                    frameElement = doc.CreateElement("compactDiscIdentifier");
                    frameElement.SetAttribute("MCDIFrameFormat", ((ID3CompactDiscIdentifierFrame)frame).MCDIFrameFormat.ToString());
                    if (((ID3CompactDiscIdentifierFrame)frame).MCDIFrameFormat == MCDIFrameFormats.ID3Standard)
                    {
                        frameElement.SetAttribute("HeaderBytes", GetByteArrayString(((ID3CompactDiscIdentifierFrame)frame).HeaderBytes));
                        frameElement.SetAttribute("LeadOutBytes", GetByteArrayString(((ID3CompactDiscIdentifierFrame)frame).LeadOutBytes));
                        // should be an ArrayList of byte[]
                        foreach (byte[] bytes in ((ID3CompactDiscIdentifierFrame)frame).TrackFrameAddresses)
                        {
                            XmlElement addressElement = doc.CreateElement("trackFrameAddress");
                            addressElement.InnerText = GetByteArrayString(bytes);
                            frameElement.AppendChild(addressElement);
                        }
                    }
                    else
                    {
                        frameElement.SetAttribute("WindowsMediaPlayerCode", ((ID3CompactDiscIdentifierFrame)frame).WindowsMediaPlayerCode);
                    }
                    break;

                case FrameTypes.MPEGLocationLookupTable:
                    break;

                case FrameTypes.Ownership:
                    break;

                case FrameTypes.Private:
                    frameElement = doc.CreateElement("private");
                    frameElement.SetAttribute("owner", ((ID3PrivateFrame)frame).OwnerIdentifier);
                    frameElement.InnerText = GetByteArrayString(((ID3PrivateFrame)frame).PrivateData);
                    break;

                case FrameTypes.PlayCounter:
                    frameElement = doc.CreateElement("playCounter");
                    frameElement.InnerText = ((ID3PlayCounterFrame)frame).Counter.ToString();
                    break;

                case FrameTypes.Popularimeter:
                    frameElement = doc.CreateElement("popularimeter");
                    frameElement.SetAttribute("email", ((ID3PopularimeterFrame)frame).EMailAddress);
                    frameElement.SetAttribute("counter", ((ID3PopularimeterFrame)frame).Counter.ToString());
                    frameElement.InnerText = ((ID3PopularimeterFrame)frame).Rating.ToString();
                    break;

                case FrameTypes.PositionSychronization:
                    break;

                case FrameTypes.RecommendedBufferSize:
                    break;

                case FrameTypes.RelativeVolumeAdjustment:
                    frameElement = doc.CreateElement("relativeVolumeAdjustment");
                    frameElement.SetAttribute("BassChannelPeakVolume", ((ID3RelativeVolumeAdjustmentFrame)frame).BassChannelPeakVolume.ToString());
                    frameElement.SetAttribute("BassChannelVolumeAdjustmentType", ((ID3RelativeVolumeAdjustmentFrame)frame).BassChannelRelativeVolumeAdjustmentType.ToString());
                    frameElement.SetAttribute("BassChannelVolumeChange", ((ID3RelativeVolumeAdjustmentFrame)frame).BassChannelRelativeVolumeChange.ToString());
                    frameElement.SetAttribute("CenterChannelPeakVolume", ((ID3RelativeVolumeAdjustmentFrame)frame).CenterChannelPeakVolume.ToString());
                    frameElement.SetAttribute("CenterChannelVolumeAdjustmentType", ((ID3RelativeVolumeAdjustmentFrame)frame).CenterChannelRelativeVolumeAdjustmentType.ToString());
                    frameElement.SetAttribute("CenterChannelVolumeChange", ((ID3RelativeVolumeAdjustmentFrame)frame).CenterChannelRelativeVolumeChange.ToString());
                    frameElement.SetAttribute("LeftbackChannelPeakVolume", ((ID3RelativeVolumeAdjustmentFrame)frame).LeftBackChannelPeakVolume.ToString());
                    frameElement.SetAttribute("LeftbackChannelVolumeAdjustmentType", ((ID3RelativeVolumeAdjustmentFrame)frame).LeftBackChannelRelativeVolumeAdjustmentType.ToString());
                    frameElement.SetAttribute("LeftbackChannelVolumeChange", ((ID3RelativeVolumeAdjustmentFrame)frame).LeftBackChannelRelativeVolumeChange.ToString());
                    frameElement.SetAttribute("LeftChannelPeakVolume", ((ID3RelativeVolumeAdjustmentFrame)frame).LeftChannelPeakVolume.ToString());
                    frameElement.SetAttribute("LeftChannelVolumeAdjustmentType", ((ID3RelativeVolumeAdjustmentFrame)frame).LeftChannelRelativeVolumeAdjustmentType.ToString());
                    frameElement.SetAttribute("LeftChannelVolumeChange", ((ID3RelativeVolumeAdjustmentFrame)frame).LeftChannelRelativeVolumeChange.ToString());
                    frameElement.SetAttribute("RightbackChannelPeakVolume", ((ID3RelativeVolumeAdjustmentFrame)frame).RightBackChannelPeakVolume.ToString());
                    frameElement.SetAttribute("RightbackChannelVolumeAdjustmentType", ((ID3RelativeVolumeAdjustmentFrame)frame).RightBackChannelRelativeVolumeAdjustmentType.ToString());
                    frameElement.SetAttribute("RightbackChannelVolumeChange", ((ID3RelativeVolumeAdjustmentFrame)frame).RightBackChannelRelativeVolumeChange.ToString());
                    frameElement.SetAttribute("RightChannelPeakVolume", ((ID3RelativeVolumeAdjustmentFrame)frame).RightChannelPeakVolume.ToString());
                    frameElement.SetAttribute("RightChannelVolumeAdjustmentType", ((ID3RelativeVolumeAdjustmentFrame)frame).RightChannelRelativeVolumeAdjustmentType.ToString());
                    frameElement.SetAttribute("RightChannelVolumeChange", ((ID3RelativeVolumeAdjustmentFrame)frame).RightChannelRelativeVolumeChange.ToString());
                    break;

                case FrameTypes.Reverb:
                    frameElement = doc.CreateElement("reverb");
                    frameElement.SetAttribute("PremixLeftToRight", ((ID3ReverbFrame)frame).PremixLeftToRight.ToString());
                    frameElement.SetAttribute("PremixRightToLeft", ((ID3ReverbFrame)frame).PremixRightToLeft.ToString());
                    frameElement.SetAttribute("ReverbBouncesLeft", ((ID3ReverbFrame)frame).ReverbBouncesLeft.ToString());
                    frameElement.SetAttribute("ReverbBouncesRight", ((ID3ReverbFrame)frame).ReverbBouncesRight.ToString());
                    frameElement.SetAttribute("ReverbFeedbackLeftToLeft", ((ID3ReverbFrame)frame).ReverbFeedbackLeftToLeft.ToString());
                    frameElement.SetAttribute("ReverbFeedbackLeftToRight", ((ID3ReverbFrame)frame).ReverbFeedbackLeftToRight.ToString());
                    frameElement.SetAttribute("ReverbFeedbackRightToLeft", ((ID3ReverbFrame)frame).ReverbFeedbackRightToLeft.ToString());
                    frameElement.SetAttribute("ReverbFeedbackRightToRight", ((ID3ReverbFrame)frame).ReverbFeedbackRightToRight.ToString());
                    frameElement.SetAttribute("ReverbLeftMS", ((ID3ReverbFrame)frame).ReverbLeftMS.ToString());
                    frameElement.SetAttribute("ReverbRightMS", ((ID3ReverbFrame)frame).ReverbRightMS.ToString());
                    break;

                case FrameTypes.SynchronizedLyrics:
                    frameElement = doc.CreateElement("synchronizedLyrics");
                    frameElement.SetAttribute("ContentType", ((ID3SynchronizedLyricsFrame)frame).ContentType.ToString());
                    frameElement.SetAttribute("Description", ((ID3SynchronizedLyricsFrame)frame).Description);
                    frameElement.SetAttribute("Language", ((ID3SynchronizedLyricsFrame)frame).Language);
                    frameElement.SetAttribute("PositionUnit", ((ID3SynchronizedLyricsFrame)frame).PositionUnit.ToString());
                    foreach (SynchronizedLyric lyric in ((ID3SynchronizedLyricsFrame)frame).SynchronizedLyrics)
                    {
                        XmlElement lyricElement = doc.CreateElement("lyric");
                        lyricElement.SetAttribute("position", lyric.Position.ToString());
                        lyricElement.InnerText = lyric.Lyric;
                        frameElement.AppendChild(lyricElement);
                    }
                    break;

                case FrameTypes.SynchronizedTempoCodes:
                    break;

                case FrameTypes.BeatsPerMinute:
                    frameElement = doc.CreateElement("beatsPerMinute");
                    frameElement.InnerText = ((ID3BeatsPerMinuteFrame)frame).BeatsPerMinute.ToString();
                    break;

                case FrameTypes.Composers:
                    frameElement = doc.CreateElement("composers");
                    foreach (String composer in ((ID3ComposersFrame)frame).Composers)
                    {
                        XmlElement composerElement = doc.CreateElement("composer");
                        composerElement.InnerText = composer;
                        frameElement.AppendChild(composerElement);
                    }
                    break;

                case FrameTypes.CopyrightMessage:
                    frameElement = doc.CreateElement("copyrightMessage");
                    frameElement.InnerText = ((ID3CopyrightMessageFrame)frame).CopyrightMessage;
                    break;

                case FrameTypes.RecordingDayMonth:
                    frameElement = doc.CreateElement("recordingDayMonth");
                    frameElement.SetAttribute("month", ((ID3RecordingDayMonthFrame)frame).Month.ToString());
                    frameElement.SetAttribute("day", ((ID3RecordingDayMonthFrame)frame).Day.ToString());
                    break;

                case FrameTypes.PlayListDelayMilliseconds:
                    frameElement = doc.CreateElement("playlistDelayMilliseconds");
                    frameElement.InnerText = ((ID3PlaylistDelayMillisecondsFrame)frame).PlaylistDelayMilliseconds.ToString();
                    break;

                case FrameTypes.EncodedBy:
                    frameElement = doc.CreateElement("encodedBy");
                    frameElement.InnerText = ((ID3EncodedByFrame)frame).EncodedBy;
                    break;

                case FrameTypes.Lyricist:
                    frameElement = doc.CreateElement("lyricist");
                    frameElement.InnerText = ((ID3LyricistFrame)frame).Lyricist;
                    break;

                case FrameTypes.FileType:
                    frameElement = doc.CreateElement("fileType");
                    frameElement.InnerText = ((ID3FileTypeFrame)frame).FileType;
                    break;

                case FrameTypes.DurationHoursMinutes:
                    frameElement = doc.CreateElement("durationHoursMinutes");
                    frameElement.InnerText = ((ID3DurationHoursMinutesFrame)frame).Duration.ToString();
                    break;

                case FrameTypes.DurationMilliseconds:
                    frameElement = doc.CreateElement("durationMilliseconds");
                    frameElement.InnerText = ((ID3DurationMillisecondsFrame)frame).DurationMilliseconds.ToString();
                    break;

                case FrameTypes.ContentGroupDescription:
                    frameElement = doc.CreateElement("contentGroupDescription");
                    frameElement.InnerText = ((ID3ContentGroupDescriptionFrame)frame).ContentGroupDescription;
                    break;

                case FrameTypes.Title:
                    frameElement = doc.CreateElement("title");
                    frameElement.InnerText = ((ID3TitleFrame)frame).Title;
                    break;

                case FrameTypes.Subtitle:
                    frameElement = doc.CreateElement("subtitle");
                    frameElement.InnerText = ((ID3SubtitleFrame)frame).Subtitle;
                    break;

                case FrameTypes.InitialKey:
                    frameElement = doc.CreateElement("initialKey");
                    frameElement.InnerText = ((ID3InitialKeyFrame)frame).InitialKey;
                    break;

                case FrameTypes.Languages:
                    frameElement = doc.CreateElement("languages");
                    frameElement.InnerText = ((ID3LanguagesFrame)frame).Languages;
                    break;

                case FrameTypes.MediaType:
                    frameElement = doc.CreateElement("mediaType");
                    frameElement.InnerText = ((ID3MediaTypeFrame)frame).MediaType;
                    break;

                case FrameTypes.OriginalAlbum:
                    frameElement = doc.CreateElement("originalAlbum");
                    frameElement.InnerText = ((ID3OriginalAlbumFrame)frame).OriginalAlbum;
                    break;

                case FrameTypes.OriginalFileName:
                    frameElement = doc.CreateElement("originalFileName");
                    frameElement.InnerText = ((ID3OriginalFileNameFrame)frame).OriginalFileName;
                    break;

                case FrameTypes.OriginalLyricist:
                    frameElement = doc.CreateElement("originalLyricist");
                    frameElement.InnerText = ((ID3OriginalLyricistFrame)frame).OriginalLyricist;
                    break;

                case FrameTypes.OriginalArtist:
                    frameElement = doc.CreateElement("originalArtist");
                    frameElement.InnerText = ((ID3OriginalArtistFrame)frame).OriginalArtist;
                    break;

                case FrameTypes.OriginalYear:
                    frameElement = doc.CreateElement("originalYear");
                    frameElement.InnerText = ((ID3OriginalYearFrame)frame).OriginalYear;
                    break;

                case FrameTypes.FileOwner:
                    frameElement = doc.CreateElement("fileOwner");
                    frameElement.InnerText = ((ID3FileOwnerFrame)frame).FileOwner;
                    break;

                case FrameTypes.Band:
                    frameElement = doc.CreateElement("band");
                    frameElement.InnerText = ((ID3BandFrame)frame).Band;
                    break;

                case FrameTypes.Conductor:
                    frameElement = doc.CreateElement("conductor");
                    frameElement.InnerText = ((ID3ConductorFrame)frame).Conductor;
                    break;

                case FrameTypes.InterpretedBy:
                    frameElement = doc.CreateElement("interpretedBy");
                    frameElement.InnerText = ((ID3InterpretedByFrame)frame).InterpretedBy;
                    break;

                case FrameTypes.PartOfSet:
                    frameElement = doc.CreateElement("partOfSet");
                    frameElement.SetAttribute("PartNum", ((ID3PartOfSetFrame)frame).PartNum.ToString());
                    frameElement.SetAttribute("PartCount", ((ID3PartOfSetFrame)frame).PartCount.ToString());
                    break;

                case FrameTypes.Publisher:
                    frameElement = doc.CreateElement("publisher");
                    frameElement.InnerText = ((ID3PublisherFrame)frame).Publisher;
                    break;

                case FrameTypes.TrackNum:
                    frameElement = doc.CreateElement("trackNum");
                    frameElement.SetAttribute("TrackNum", ((ID3TrackNumFrame)frame).TrackNum.ToString());
                    frameElement.SetAttribute("TrackCount", ((ID3TrackNumFrame)frame).TrackCount.ToString());
                    break;

                case FrameTypes.RecordingDates:
                    frameElement = doc.CreateElement("recordingDates");
                    frameElement.InnerText = ((ID3RecordingDatesFrame)frame).RecordingDates;
                    break;

                case FrameTypes.InternetRadioStationName:
                    frameElement = doc.CreateElement("internetRadioStationName");
                    frameElement.InnerText = ((ID3InternetRadioStationNameFrame)frame).InternetRadioStationName;
                    break;

                case FrameTypes.InternetRadioStationOwner:
                    frameElement = doc.CreateElement("internetRadioStationOwner");
                    frameElement.InnerText = ((ID3InternetRadioStationOwnerFrame)frame).InternetRadioStationOwner;
                    break;

                case FrameTypes.SizeBytes:
                    frameElement = doc.CreateElement("sizeBytes");
                    frameElement.InnerText = ((ID3SizeBytesFrame)frame).SizeBytes.ToString();
                    break;

                case FrameTypes.InternationalStandardRecordingCode:
                    frameElement = doc.CreateElement("internationalStandardRecordingCode");
                    frameElement.InnerText = ((ID3InternationalStandardRecordingCodeFrame)frame).InternationalStandardRecordingCode;
                    break;

                case FrameTypes.EncoderSettings:
                    frameElement = doc.CreateElement("encoderSettings");
                    frameElement.InnerText = ((ID3EncoderSettingsFrame)frame).EncoderSettings;
                    break;

                case FrameTypes.UserDefinedText:
                    frameElement = doc.CreateElement("userDefinedText");
                    frameElement.InnerText = ((ID3UserDefinedTextFrame)frame).UserDefinedText;
                    break;

                case FrameTypes.UniqueFileID:
                    frameElement = doc.CreateElement("uniqueFileID");
                    frameElement.SetAttribute("ownerID", ((ID3UniqueFileIDFrame)frame).OwnerIdentifier);
                    frameElement.InnerText = GetByteArrayString(((ID3UniqueFileIDFrame)frame).Identifier);
                    break;

                case FrameTypes.TermsOfUse:
                    frameElement = doc.CreateElement("termsOfUse");
                    frameElement.InnerText = ((ID3TermsOfUseFrame)frame).TermsOfUse;
                    break;

                case FrameTypes.UnsyncedLyrics:
                    frameElement = doc.CreateElement("unsyncedLyrics");
                    frameElement.InnerText = ((ID3UnsynchedLyricsFrame)frame).UnsynchedLyrics;
                    break;

                case FrameTypes.CommercialInformationWebAddress:
                    frameElement = doc.CreateElement("commercialInformationWebAddress");
                    frameElement.InnerText = ((ID3CommercialInformationWebAddressFrame)frame).WebAddress;
                    break;

                case FrameTypes.CopyrightInformationWebAddress:
                    frameElement = doc.CreateElement("copyrightInformationWebAddress");
                    frameElement.InnerText = ((ID3CopyrightInformationWebAddressFrame)frame).WebAddress;
                    break;

                case FrameTypes.OfficialAudioFileWebAddress:
                    frameElement = doc.CreateElement("officialAudioFileWebAddress");
                    frameElement.InnerText = ((ID3OfficialAudioFileWebAddressFrame)frame).WebAddress;
                    break;

                case FrameTypes.OfficialArtistWebAddress:
                    frameElement = doc.CreateElement("officialArtistWebAddress");
                    frameElement.InnerText = ((ID3OfficialArtistWebAddressFrame)frame).WebAddress;
                    break;

                case FrameTypes.OfficialAudioSourceWebAddress:
                    frameElement = doc.CreateElement("officialAudioSourceWebAddress");
                    frameElement.InnerText = ((ID3OfficialAudioSourceWebAddressFrame)frame).WebAddress;
                    break;

                case FrameTypes.OfficialInternetRadioStationWebAddress:
                    frameElement = doc.CreateElement("officialInternetRadioStationWebAddress");
                    frameElement.InnerText = ((ID3OfficialInternetRadioStationWebAddressFrame)frame).WebAddress;
                    break;

                case FrameTypes.PaymentWebAddress:
                    frameElement = doc.CreateElement("paymentWebAddress");
                    frameElement.InnerText = ((ID3PaymentWebAddressFrame)frame).WebAddress;
                    break;

                case FrameTypes.PublisherWebAddress:
                    frameElement = doc.CreateElement("publisherWebAddress");
                    frameElement.InnerText = ((ID3PublisherWebAddressFrame)frame).WebAddress;
                    break;

                case FrameTypes.UserDefinedWebAddress:
                    frameElement = doc.CreateElement("userDefinedWebAddress");
                    frameElement.SetAttribute("description", ((ID3UserDefinedWebAddressFrame)frame).Description);
                    frameElement.InnerText = ((ID3UserDefinedWebAddressFrame)frame).WebAddress;
                    break;
            }
            return frameElement;
        }


        private static String GetByteArrayString(byte[] bytes)
        {
            if (bytes.Length == 0)
            {
                return "";
            }
            StringBuilder value = new StringBuilder();
            foreach (byte b in bytes)
            {
                value.Append(b.ToString("X"));
                value.Append(" ");
            }
            value.Remove(value.Length - 1, 1);
            return value.ToString();
        }
    }
}
